// 中断向量表初始化
// 对外部函数的引用
extern "C" {
    fn set_idt_handler(no : u8, handler : *const u8, arg_num : u8, attr: u8);
    fn idt_init_asm();
    fn print_str_endwith_enter(p : *const u8, l : u32);
    fn out_port_byte(port : u16, value : u8);
    fn idt_timer_handler();
    fn idt_system_call_handler();
    fn system(index: u32) -> u32;
    fn in_port_byte(port : u16) -> u8;
    fn idt_keyboard_handler();
}

// IDT描述符表初始化
pub fn idt_init() {
    unsafe {
        idt_init_asm();
        init_8259A();
        init_8253();

        enable_hardware_interupts();

        let _dispstr = "IDT init ok.";
        print_str_endwith_enter(_dispstr.as_ptr(), _dispstr.len().try_into().unwrap());
    }
}

fn enable_hardware_interupts() {
    // 开启硬件中断
    enable_irq(IRQ0_TIMER_MASK);
    enable_irq(IRQ1_KEYBOARD_MASK);
}

// 8029A中断控制器初始化
#[allow(non_snake_case)]    // 允许non_snake_case函数命名方式
fn init_8259A() {
    unsafe {
        out_port_byte(0x20, 0x11); // 向主8259A写ICW1，011h表示8字节中断向量、级联8259A、需要ICW4
        out_port_byte(0xA0, 0x11); // 向从8259A写ICW1，011h表示8字节中断向量、级联8259A、需要ICW4

        out_port_byte(0x21, 0x20); // 向主8259A写ICW2，020h表示IRQ0对应的中断向量号为0x20，意味着，IRQ1-IRQ7中断向量号分别是0x21-0x27
        out_port_byte(0xA1, 0x28); // 向从8259A写ICW2，028h表示IRQ0对应的中断向量号为0x28，意味着，IRQ1-IRQ7中断向量号分别是0x29-0x2F

        out_port_byte(0x21, 0x04); // 向主8259A写ICW3，004h表示主8259A的IR2级联从片
        out_port_byte(0xA1, 0x02); // 向从8259A写ICW3，002h表示从8259A连接到主8259A的IR2上

        out_port_byte(0x21, 0x01); // 向主8259A写ICW4，001h表示80x86模式
        out_port_byte(0xA1, 0x01); // 向主8259A写ICW4，001h表示80x86模式

        // 初始化完成
        // 可以控制仅开启部分中断
        out_port_byte(0x21, 0xFF); // 临时关闭所有中断, 0表示打开中断
        out_port_byte(0xA1, 0xFF); // 屏蔽从8259A所有中断, 从8259A写OCW1
    }
}

const SYS_IDT_DESC_ATTR: u8 = 0x8E;
const USER_IDT_DESC_ATTR: u8 = 0xEE;
// 被idt_init_asm调用，中断向量表初始化时会安装不同中断向量对应的处理函数
#[no_mangle] // 不重整函数名 !!! 注意必须要使用它
pub extern "C" fn handler_init() {
    unsafe {
        set_idt_handler(0x20, idt_timer_handler as *const _, 0, SYS_IDT_DESC_ATTR); // 安装定时中断处理函数
        set_idt_handler(0x21, idt_keyboard_handler as *const _, 0, SYS_IDT_DESC_ATTR); // 安装键盘中断处理函数
        set_idt_handler(0x80, idt_system_call_handler as *const _, 0, USER_IDT_DESC_ATTR); // 安装系统中断处理函数，DPL要设置为用户态
    }
}

const SYSCALL_GET_TICKS: u32 = 0;
pub fn get_ticks() -> u32 {
    unsafe {
        system(SYSCALL_GET_TICKS)
    }
}


// 定时器Intel 8253初始化
const COUNTER0_PORT: u16 = 0x40;
const CTRL_PORT: u16 = 0x43;
const FREQ_OF_INPUT: u32 = 1193180;
const TIMER_FREQ: u16 = 10;  // 10ms
const TIMER_COUNT: u16 = (FREQ_OF_INPUT / ((1000 / TIMER_FREQ) as u32)) as u16;
const TIMER_CTRL_CMD: u8 = 0x34; // 00 11 010 0 
fn init_8253() {
    unsafe {
        // 对43H端口输出控制命令字，值为00 11 010 0，表示选择Counter0，先读写低字节再读写高字节，选择模式2，使用二进制计数
        out_port_byte(CTRL_PORT, TIMER_CTRL_CMD);

        // 对40H端口输出低字节
        out_port_byte(COUNTER0_PORT, (TIMER_COUNT & 0xFF) as u8);

        // 对40H端口输出高字节
        out_port_byte(COUNTER0_PORT, ((TIMER_COUNT >> 8) & 0xFF) as u8);
    }
}


const IRQ0_TIMER_MASK: u8 = 0x00;            // 时钟中断
const IRQ1_KEYBOARD_MASK: u8 = 0x01;         // 键盘中断

fn enable_irq(irq_no: u8) {
    let no;
    let port;
    if irq_no >= 8 { // 从8259A
        no = irq_no - 8;
        port = 0xA1;
    } else {    // 主8259A
        no = irq_no;
        port = 0x21;
    }

    unsafe {
        let current = in_port_byte(port);
        out_port_byte(port, (!((0x01 as u8) << no)) & current);
    }
}

/*
fn disable_irq(irq_no: u8) {
    let no;
    let port;
    if irq_no >= 8 { // 从8259A
        no = irq_no - 8;
        port = 0xA1;
    } else {    // 主8259A
        no = irq_no;
        port = 0x21;
    }

    unsafe {
        let current = in_port_byte(port);
        out_port_byte(port, ((0x01 as u8) << no) | current);
    }
}
*/
